package utils

import (
	"bytes"
	"fmt"
	"os/exec"
	"reflect"
)

type Command struct {
	ExecutePath string
	Args        []string
}

// ExecCmd 执行命令行，并且返回标准输出和标准错误
// workDir 命令执行时的目录
// env 命令执行时的环境变量
// stdin 执行命令所需的输入
// execPath 所需执行的命令，尽量使用绝对路径
// args 执行参数
func ExecCmd(
	workDir string,
	env []string,
	stdin bytes.Buffer,
	execPath string,
	args ...string,
) (bytes.Buffer, error) {
	cmd := exec.Command(execPath, args...)
	env = append([]string{"SHELL=/bin/bash"}, env...)
	cmd.Dir = workDir
	cmd.Env = env
	cmd.Stdin = &stdin

	var stdout, stderr bytes.Buffer
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	Logger.Info(fmt.Sprintf("executing 「%s」", cmd))
	Logger.Debug(fmt.Sprintf("cmd:「%s」,wordDir:「%s」,env:「%s」,stdin:「%s」", cmd, workDir, env, stdin.String()))

	err := cmd.Run()
	if err != nil {
		Logger.Error(fmt.Sprintf("cmd:「%s」,executed error:「%s」,stdout: 「%s」,stderr: 「%s」", cmd, err, stdout.String(), stderr.String()))
		return bytes.Buffer{}, err
	}
	Logger.Debug(fmt.Sprintf("cmd 「%s」 executed done，stdout: 「%s」,stderr: 「%s」", cmd, stdout.String(), stderr.String()))
	if reflect.DeepEqual(stderr, bytes.Buffer{}) {
		// TODO 这里逻辑是当stderr不为空的时候，返回stderr作为error，但是该逻辑是stderr和bytes.Buffer{}一样时，返回错误
		err = fmt.Errorf(stderr.String()) // todo 这里添加性参数 确定非标准输出后是否返回error
		Logger.Warning(err)
		return bytes.Buffer{}, err
	}

	return stdout, nil
}

func ExecCmdChains(
	workDir string,
	env []string,
	stdin bytes.Buffer,
	chains []Command,
) (bytes.Buffer, error) {

	nextStdIn := stdin
	var finalOut bytes.Buffer
	for _, chain := range chains {
		stdout, err := ExecCmd(workDir, env, nextStdIn, chain.ExecutePath, chain.Args...)
		if err != nil {
			return bytes.Buffer{}, err
		}
		nextStdIn = stdout
		finalOut = stdout
	}
	return finalOut, nil
}
